package com.jidolstar.astro
{
	/**
	 * 천문학 관련 Date 클래스
	 * 
	 * <ul> 
	 * <li>날짜와 Julian Date 변환이 가능하며 윤년, 해당 해의 날수, 해당 달(월)의 날수, 요일 등 계산이 가능하다.
	 * <li>그레고리력과 율리우스력 자동변환을 한다.
	 * <li>Modified Julian Date, Reduced Julian Date 값을 알아낼 수 있다. 
	 * </ul>
	 * 
	 * @author Yongho,Ji (jidolstar@gmail.com)
	 * @since 2009.04.06
	 * 
	 * @see
	 * http://en.wikipedia.org/wiki/Julian_day
	 * http://www.bambooweb.com/articles/j/u/Julian_day.html
	 * http://scienceworld.wolfram.com/astronomy/JulianDate.html
	 * http://wwwmacho.mcmaster.ca/JAVA/CD.html
	 * http://www.hermetic.ch/cal_stud/jdn.htm
	 * http://www.cv.nrao.edu/~rfisher/Ephemerides/times.html
	 * http://en.wikipedia.org/wiki/Universal_Time
	 * http://www.merlyn.demon.co.uk/daycount.htm#GDDC
	 * 
	 * @example
	 * <listing version="3.0">
	 * var date:ALDate; 
	 * date = new ALDate( 2009, 4, 8, 3, 18, 4, ALCalendarType.GREGORIAN ); //입력하는 년월일시분초는 Greogian 달력을 기준으로 하는 값임. 이 경우에는 null로 입력해도 동일   
	 * trace( date.toString() ); //2009-04-08 03:18:04.000
	 * trace( date.JD ); //2454929.637546296
	 * trace( date.JDN ); //2454929
	 * trace( date.dayOfWeek ); //3 : 수요일 
	 * trace( date.dayOfYear ); //98 : 2009년 1월 1일부터 2009년 4월 8일까지 98일임  
	 * trace( date.daysInYear ); //365 : 2009년은 365일임 
	 * trace( date.fractionalYear ); //2009.2661302638253 : 년도를 분수형태로 나타냄 
	 * trace( date.isLeapYear ); //false : 윤년이 아님 
	 * trace( date.MJD ); //54929.13754629623 : Modified Julian Date값
	 * trace( date.year, date.month, date.day, date.hour, date.minute, date.second ); //년,월,일,시,분,초
	 * trace( date.datePartOfJD ); //2454929.5 일 
	 * trace( date.timePartOfJD ); //3.3011111095547676 h 
	 * 
	 * date = new ALDate( 2000, 1, 1, 0, 0, 0 );
	 * trace( date.toString() ); //2000-01-01 00:00:00.000
	 * trace( date.JD ); //2451544.5
	 * trace( date.JDN ); //2451544
	 * trace( date.dayOfWeek ); //6 : 토요일 
	 * trace( date.dayOfYear ); //1 : 2009년 1월 1일부터 2009년 1월 1일까지 1일임  
	 * trace( date.daysInYear ); //366 : 2009년은 365일임 
	 * trace( date.fractionalYear ); //2000 : 년도를 분수형태로 나타냄 
	 * trace( date.isLeapYear ); //true : 윤년임
	 * trace( date.MJD ); //51544 : Modified Julian Date값
	 * trace( date.year, date.month, date.day, date.hour, date.minute, date.second ); //년,월,일,시,분,초 
	 * trace( date.datePartOfJD ); //2451544.5 일
	 * trace( date.timePartOfJD ); //0h
	 * 
	 * date = new ALDate( 1988, 1, 27, null );
	 * trace( date.toString() ); //1988-01-27 00:00:00.000
	 * trace( date.JD ); //2447187.5
	 * 
	 * date = new ALDate( 837, 4, 10.3 );
	 * trace( date.toString() ); //837-04-10 07:12:00.000
	 * trace( date.JD ); //2026871.8
	 * 
	 * date = new ALDate( 2305812.5 );
	 * trace( date.toString() ); //1600-12-31 00:00:00.000
	 * trace( date.JD ); //2305812.5
	 * 
	 * date = new ALDate( 0 );
	 * trace( date.toString() ); //-4712-01-01 12:00:00.000
	 * trace( date.JD ); //0
	 * 
	 * date = new ALDate( 2400000.5 );
	 * trace( date.toString() ); //1858-11-17 00:00:00.000
	 * trace( date.JD ); //2400000.5
	 * 
	 * trace( ALDate.date2JD( 837, 4, 10.3 ) ); //2026871.8	
	 * 
	 * trace( ALDate.JD2MJD( 2451544.5 ) ); //51544
	 * 
	 * trace( ALDate.isLeapYear( 2000 ) ); //true
	 * </listing>
  	 */ 
	public class ALDate
	{
		private var _jd:Number;
		private var _calendarType:String;
		private var isSetDate:Boolean;
		private var _year:int;
		private var _month:int;
		private var _day:int;
		private var _hour:int;
		private var _minute:int;
		private var _second:Number;
		
		/**
		 * 생성자.
		 * @param ...args
		 * @see setDate 
		 * @throw ArgumentError
		 */ 
		public function ALDate( ...args )
		{
			try
			{
				switch( args.length )
				{
					case 0:
						setDate0();
						break;
					case 1:
						setDate1( args[0] );
						break;
					case 3:
						setDate4( args[0], args[1], args[2], null );
						break;
					case 4:
						setDate4( args[0], args[1], args[2], args[3] );
						break;
					case 6:
						setDate7( args[0], args[1], args[2], args[3], args[4], args[5], null );
						break;
					case 7:
						setDate7( args[0], args[1], args[2], args[3], args[4], args[5], args[6] );
						break;
					default:
						throw new ArgumentError( "인자 수가 주어진 형태에 맞지 않습니다." );
						break;
				}
			}
			catch( e:Error )
			{
				throw new ArgumentError( e.message );
			}
		}
		
		/**
		 * 날짜를 설정한다. 
		 * @param ...args
		 * <ul>
		 * 		<li>지정하지 않을 경우, 현재의 UTC시간과 날짜로 설정
		 * 		<li>1개 값인 경우 Number형태인 JulianDate값 또는 Date객체 또는 ALCalendarDate객체 중 하나이어야함. 단 Julian Date는 0이상 값이여야 한다.
		 * 		<li>3개 값인 경우 year, month, day가 설정됨, calendar type은 날짜에 따라 달라짐. 여기서 day는 시간값도 포함한다(32.44일 이 가능)
		 * 		<li>4개 값인 경우 year, month, day, calendar type 값으로 설정함. 여기서 day는 시간값도 포함한다(32.44일 이 가능) 
		 * 		<li>6개 값인 경우 year, month, day, hour, minute, second 값으로 설정함. calendar type은 날짜에 따라 달라짐
		 * 		<li>7개 값인 경우 year, month, day, hour, minute, second, calendar type 값으로 설정함 
		 * </ul>
		 * @throw ArgumentError
		 */ 
		public function setDate( ...args ):void
		{
			try
			{
				switch( args.length )
				{
					case 0:
						setDate0();
						break;
					case 1:
						setDate1( args[0] );
						break;
					case 3:
						setDate4( args[0], args[1], args[2], null );
						break;
					case 4:
						setDate4( args[0], args[1], args[2], args[3] );
						break;
					case 6:
						setDate7( args[0], args[1], args[2], args[3], args[4], args[5], null );
						break;
					case 7:
						setDate7( args[0], args[1], args[2], args[3], args[4], args[5], args[6] );
						break;
					default:
						throw new ArgumentError( "인자 수가 주어진 형태에 맞지 않습니다." );
						break;
				}
			}
			catch( e:Error )
			{
				throw new ArgumentError( e.message );
			}
			
		}
		
		/**
		 * @private
		 */ 
		private function setDate0():void
		{
			var date:Date = new Date();
			_year = date.getUTCFullYear();
			_month = date.getUTCMonth() + 1;
			_day = date.getUTCDate();
			_hour = date.getUTCHours();
			_minute = date.getUTCMinutes();
			_second = date.getUTCSeconds() + date.getUTCMilliseconds() / 1000.0;
			this.JD = date2JD( _year, _month, _day, _hour, _minute, _second, null );
			isSetDate = true;
		}
		
		/**
		 * @private
		 */ 
		private function setDate1( param:* ):void
		{
			//Julian Date
			if( param is Number )
			{
				this.JD = param as Number;
				 
				isSetDate = false;
			}
			//Date
			else if( param is Date )
			{
				var date:Date = param as Date;
				_year = date.getUTCFullYear();
				_month = date.getUTCMonth() + 1;
				_day = date.getUTCDate();
				_hour = date.getUTCHours();
				_minute = date.getUTCMinutes();
				_second = date.getUTCSeconds() + date.getUTCMilliseconds() / 1000.0;
				this.JD = date2JD( _year, _month, _day, _hour, _minute, _second, null );
				isSetDate = true;				
			}
			//ALCalendarDate
			else if( param is ALCalendarDate )
			{
				var calDate:ALCalendarDate = param as ALCalendarDate;
				_year = calDate.year;
				_month = calDate.month;
				_day = calDate.day;
				_hour = calDate.hour;
				_minute = calDate.minute;
				_second = calDate.second;
				this.JD = date2JD( _year, _month, _day, _hour, _minute, _second, null );
				isSetDate = true;					
			}
			else
			{
				throw new ArgumentError( "첫번째 인자는 Julian Date값이거나 Date 또는 ALCalendar 형태이어야 합니다." );
			}
		}	
		
		/**
		 * @private
		 */ 
		private function setDate4( year:int, month:int, fractionalDay:Number, calendarType:String = null ):void
		{
			var day:int = Math.floor(fractionalDay);
			var frac:Number = fractionalDay - day;
			if( frac == 0 )
			{
				this.JD = date2JD( year, month, day, 0, 0, 0, calendarType );
			}
			else
			{
				var temp:Number = frac * 24;
				var hour:int = Math.floor(temp);
				temp = (temp - hour) * 60;
				var minute:int = Math.floor(temp);
				var second:Number = (temp - minute) * 60;
				this.JD = date2JD( year, month, day, hour, minute, second, calendarType );
			}
		}
						
		/**
		 * @private
		 */ 
		private function setDate7( year:int, month:int, day:int, hour:int, minute:int, second:Number, calendarType:String = null ):void
		{
			this.JD = date2JD( year, month, day, hour, minute, second, calendarType );
		}
		
		/**
		 * year, month, day를 계산한다. 단, Julian Date가 0 이상이라는 가정하에서 가능하다. 
		 * @see Astronomical Algorithms in Meeus P.63
		 */ 
		private function setDateFromJD():void
		{
			if( !isSetDate )
			{
				var jd:Number = _jd + 0.5;
				var Z:Number = Math.floor( jd );
				var F:Number = jd - Z;
				var A:Number;
				if( Z < 2299161 )
				{
					A = Z;
					_calendarType = ALCalendarType.JULIAN;
				}
				else
				{
					var a:Number = Math.floor( (Z - 1867216.25) / 36524.25);
					A = Z + 1 + a - Math.floor( a / 4.0 );
					_calendarType = ALCalendarType.GREGORIAN;
				}
				var B:int = A  + 1524;
				var C:int = Math.floor( (B - 122.1) / 365.25);
				var D:int = Math.floor( 365.25 * C);
				var E:int = Math.floor( (B - D) / 30.6001);		
				var fractionalDay:Number;
				fractionalDay = B - D - Math.floor( 30.6001 * E ) + F; //계산의 정확성을 위해 30.6001 대신 30.6을 넣지 말자.
				_day = Math.floor( fractionalDay );
				_month = ( E < 14 )? E-1 : E-13;
				_year = ( _month > 2 ) ? C - 4716: C - 4715;
				
				if( F == 0 )
				{
					_hour = 0;
					_minute = 0;
					_second = 0;
				}
				else
				{
					var temp:Number = (fractionalDay - _day) * 24;
					_hour = Math.floor(temp);
					temp = (temp - _hour) * 60;
					_minute = Math.floor( temp );
					_second = (temp - _minute) * 60;			
				}
				isSetDate = true;
			}
		}		
		
		/**
		 * Julian Date 
		 * @throw ArgumentError Julian Date값이 0보다 작은 값이 들어오는 것은 처리할 수 없다.
		 */ 
		public function get JD():Number
		{
			return _jd;
		}
		
		/**
		 * @private  
		 */ 
		public function set JD( jd:Number ):void
		{
			if( jd < 0 )
			{
				throw new ArgumentError( "Julian Date값은 0보다 큰 값이어야 한다. 왜냐하면 현재로선 JD<0값으로 Year, month, day계산이 안되기 때문이다. Astronomical algorithmes 2nd p63 참고" );
			}			
			_jd = jd;
			isSetDate = false;
		}
		
		/**
		 * Julian Date number
		 */ 
		public function get JDN():Number
		{
			return Math.floor(_jd);
		}
		
		/**
		 * Julian Date에서 날짜부분만 얻어냄. 234311.5이면 234311.5이다.
		 */ 
		public function get datePartOfJD():Number
		{
			return Math.floor( _jd - 0.5 ) + 0.5;
		}
		
		/**
		 * Julian Date에서 시간부분만 얻어냄. 234311.5이면 0h임.
		 */ 
		public function get timePartOfJD():Number
		{
			return ( _jd - datePartOfJD ) * 24.0;
		}
		
		/**
		 * Modified Julian Date 
		 */ 
		public function get MJD():Number
		{
			return ALDate.JD2MJD( _jd );
		}
		
		/**
		 * @private  
		 */ 
		public function set MJD( mjd:Number ):void
		{
			this.JD = ALDate.MJD2JD( mjd );
		}
		
		/**
		 * Reduced Julian Date 
		 */ 
		public function get RJD():Number
		{
			return ALDate.JD2RJD( _jd );			
		}
		
		
		/**
		 * @private  
		 */ 
		public function set RJD( rjd:Number ):void
		{
			this.JD = ALDate.RJD2JD( rjd );
		}
		
		/**
		 * 년도 
		 */ 
		public function get year():int
		{
			if( isSetDate == false ) setDateFromJD();
			return _year;
		}

		/**
		 * 월(1~12)
		 */ 
		public function get month():int
		{
			if( isSetDate == false ) setDateFromJD();
			return _month;
		}

		/**
		 * 일(1~31)
		 */ 
		public function get day():int
		{
			if( isSetDate == false ) setDateFromJD();
			return _day;
		}

		/**
		 * 시(0~23)
		 */ 
		public function get hour():int
		{
			if( isSetDate == false ) setDateFromJD();
			return _hour;
		}

		/**
		 * 분(0~59)
		 */ 
		public function get minute():int
		{
			if( isSetDate == false ) setDateFromJD();
			return _minute;
		}

		/**
		 * 초(0~59.9999...)
		 */ 
		public function get second():Number
		{
			if( isSetDate == false ) setDateFromJD();
			return _second;
		}
		
		/**
		 * 현재 설정된 달력의 종류
		 * 1582.10.4 이전이면 ALCalendarType.JULIAN, 
		 * 1582.10.15 이후이면 ALCalendarType.GREGORIAN 이 설정된다.
		 *  
		 */ 
		public function get currentCalendarKind():String
		{
			return _calendarType;
		}
		
		/**
		 * 복제한다. 
		 */ 
		public function get clone():ALDate
		{
			return new ALDate( _jd );
		}
		
		/**
		 * CalendayDate 타입으로 날짜를 반환해준다. 
		 */ 
		public function get calendarDate():ALCalendarDate
		{
			return new ALCalendarDate( year, month, day, hour, minute, second );
		}
		
		/**
		 * 분수값을 가지는 년도값 반환 
		 * 가령 1932.3221년 
		 */ 
		public function get fractionalYear():Number
		{
			return year + (_jd - date2JD( year, 1, 1, 0, 0, 0 ) ) / daysInYear;
		}
		
		/**
		 * 분수값을 가지는 day 반환
		 * 
		 */ 
		public function get fractionalDay():Number
		{
			return day + hour/24.0 + minute/1440.0 + second/86400.0;
		}
		
		/**
		 * 윤년인가?
		 */ 
		public function get isLeapYear():Boolean
		{
			if( year % 4 == 0 )
			{
				if( year % 100 == 0 )
				{
					if( currentCalendarKind == ALCalendarType.GREGORIAN )
					{
						if( year % 400 == 0 )
							return true;
						return false;
					}
					else
					{
						return false;
					}
				}
				return true;
			}
			return false;
		}

		/**
		 * 해당 년도에서  현재 날짜까지의 날수 
		 * 
		 * @see Astronomical Algorithms in Meeus, P65
		 */ 
		public function get dayOfYear():int
		{
			var K:int = isLeapYear ? 1 : 2;
			var N:int = Math.floor( 275 * month / 9 ) - K * Math.floor( ( month + 9 ) / 12 ) + day - 30; 
			return N;
		}
		
		/**
		 * 해당 해의 날수 
		 */ 
		public function get daysInYear():int
		{
			if( isLeapYear )
				return 366;
			else
				return 365;
		}
		
		/**
		 * 해당 달의 날 수 
		 */ 
		public function get daysInMonth():int
		{
			var monthLength:Array = 
			[     
				31, 28, 31, 30, 31, 30,
    			31, 31, 30, 31, 30, 31
    		];
    		
    		if( isLeapYear )
    			monthLength[1]++;
    		return monthLength[_month-1];
		}
		
		/**
		 * 요일 
		 * <p>
		 * 0 : ALDayOfWeek.SUNDAY<br>
		 * 1 : ALDayOfWeek.MONDAY<br>
		 * 2 : ALDayOfWeek.THUSEDAY<br>
		 * 3 : ALDayOfWeek.WEDNESDAY<br>
		 * 4 : ALDayOfWeek.THURSDAY<br>
		 * 5 : ALDayOfWeek.FRIDAY<br>
		 * 6 : ALDayOfWeek.SATURDAY<br>
		 * </p>
		 * @see Astronomical Algorithms in Meeus, P65
		 */ 
		public function get dayOfWeek():int
		{
			return Math.floor(_jd + 1.5) % 7;
		}
		
		/**
		 * year-month-day hour:minute:second 출력 
		 */ 
		public function toString( arcsecToFixed:int = 2 ):String
		{
			if( !isSetDate ) setDateFromJD();
			var month:String = _month < 10 ? "0"+_month : _month.toString();
			var day:String = _day < 10 ? "0"+_day : _day.toString();
			var hour:String = _hour < 10 ? "0"+_hour : _hour.toString();
			var minute:String = _minute < 10 ? "0" + _minute : _minute.toString();
			var second:String = _second < 10 ? "0" + _second.toFixed( arcsecToFixed ) : _second.toFixed( arcsecToFixed );
			return _year + "-" + month + "-" + day + " " + hour + ":" + minute + ":" + second;
		}


		/**
		 * Calculation of the JD from the General Date 
		 * @param year 년도
		 * @param month 월(1~12)
		 * @param day 일(1~31)
		 * @param hour 시간(0~23)
		 * @param minute 분(0~59)
		 * @param seconds 초
		 * @param calendarType 
		 * 		달력 타입. 기본값은 null이다. 
		 * 		특별한 경우에 ALCalendarType.GREGORIAN 또는 ALCalendarType.JULIAN 를 설정한다.
		 * 		<ul>
		 * 		
		 * 			<li> null값을 설정하는 경우 자동으로 1582.10.04 이전은 Julian Calendar를 적용해 계산하고 1582.10.15 이후에는 Gregorian Calandar를 적용해 계산한다. 
		 * 		 		만약 1582.10.05부터  1582.10.14내의 날짜로 맞추면 ArgumentError가 발생한다.
		 * 			<li> CalendarKind.GREGORIAN로 설정하는 경우 날짜는 Gregorian Calendar로 간주하고 Julian Date를 계산한다. 
		 * 	 		<li> CalendarKind.JULIAN로 설정하는 경우Julian Calendar로 간주하고 Julian Date를 계산한다.
		 * 		</ul>
		 * @return Julian Date 값을 반환한다. 
		 * @see Astronomical Algorithms in Meeus P.61
		 */ 		
		static public function date2JD( year:int, month:int, day:int, hour:int=0, minute:int=0, second:Number=0, calendarType:String = null ):Number
		{
			// 1. Gregorian Calendar인지  Julian Calendar인지 판단한다.
			// 2. 1582.10.5 ~ 1582.10.14 이면 ArgumentError 발생 
			if( calendarType == null )
			{
				if( year > 1582 ) 
				{
					calendarType = ALCalendarType.GREGORIAN;
				} 
				else if( year < 1582 )
				{
					calendarType = ALCalendarType.JULIAN;
				}
				else 
				{
					if( month > 10 )
					{
						calendarType = ALCalendarType.GREGORIAN;
					}
					else if( month < 10 )
					{
						calendarType = ALCalendarType.JULIAN;
					}
					else
					{
						if( day >= 15 )
						{
							calendarType = ALCalendarType.GREGORIAN;
						}
						else if( day <= 4 )
						{
							calendarType = ALCalendarType.JULIAN;
						}
						else
						{
							throw ArgumentError( "The date from 1582.10.05 to 1582.10.14 can't use" );
						}
					}
				}
			}
			var A:Number = 0;
			var B:Number = 0;
			if( month <= 2 ) 
			{
				month += 12;
				year--;	
			}
			
			//달력 종류에 따라 계산 방법이 다르다.
			if( calendarType == ALCalendarType.GREGORIAN )
			{
				A = Math.floor( year /100 );
				B = 2 - A + Math.floor( A / 4 );				
			}
			
			var Ds:Number = day + hour/24 + minute/1440 + second/86400;
			return 	Math.floor( 365.25 * ( year + 4716 ) )+ 
					Math.floor( 30.6001 * ( month + 1) ) +
					Ds + B - 1524.5; 			
		} 
		
		/**
		 * Calculation of the the General Date from JD
		 */ 
		static public function JD2Date( JD:Number ):ALDate
		{
			var date:ALDate = new ALDate( JD );
			return date;
		} 

		/**
		 * Calculation of the MJD from the JD 
		 * @param JD
		 * @return MJD
		 */ 
		static public function JD2MJD( jd:Number ):Number
		{
			return jd - 2400000.5;
		}
		
		/**
		 * Calculation of the JD from the MJD 
		 * @param MJD
		 * @return JD
		 */ 
		static public function MJD2JD( mjd:Number ):Number
		{
			return mjd + 2400000.5;
		} 
		
		/**
		 * Calculation of the RJD from the JD 
		 * @param JD
		 * @return RJD
		 */ 		
		static public function JD2RJD( jd:Number ):Number
		{
			return jd - 2400000;
		}
		
		/**
		 * Calculation of the JD from the RJD 
		 * @param RJD
		 * @return JD
		 */ 
		static public function RJD2JD( rjd:Number ):Number
		{
			return rjd + 2400000;
		}
		
		/**
		 * 해당 년도가 윤년인가?
		 */ 
		static public function isLeapYear( year:int ):Boolean
		{
			if( year % 4 == 0 )
			{
				if( year % 100 == 0 )
				{
					if( year > 1582 )
					{
						if( year % 400 == 0 )
							return true;
						return false;
					}
					else
					{
						return false;
					}
				}
				return true;
			}
			return false;			
		}
		
		/**
		 * 해당 년도에서 현재 날짜까지의 날수 
		 * @param year
		 * @param month
		 * @param day
		 * @return day of the year
		 * @see Astronomical Algorithms in Meeus, P65		 
		 */ 
		static public function dayOfYear( year:int, month:int, day:int ):Number
		{
			var K:int = ALDate.isLeapYear( year ) ? 1 : 2;
			var N:int = Math.floor( 275 * month / 9 ) - K * Math.floor( ( month + 9 ) / 12 ) + day - 30; 
			return N;			
		}
			
		/**
		 * calculation of 'day of the year' to 'day and month'
		 * @param year 년도 
		 * @param dayOfYear 해당 년도 1월 1일부터의 날수 
		 * @return [ month, day of month ] 
		 * @see Astronomical Algorithms in Meeus, P66	
		 */ 
		static public function dayOfYearToDayAndMonth( year:int, dayOfYear:int ):Array
		{
			var K:Number = ALDate.isLeapYear( year ) ? 1 : 2;
			
			var month:int = Math.floor( 9 * (K + dayOfYear) / 275.0 + 0.98);
			if (dayOfYear < 32)
				month = 1;
			
			var dayOfMonth:int = dayOfYear - Math.floor((275*month)/9.0) + (K*Math.floor((month + 9)/12.0)) + 30;
			return [ month, dayOfMonth ];			
		}		
		

		/**
		 * 해당 해의 날수 
		 */ 
		static public function daysInYear( year:int ):int
		{
			if( ALDate.isLeapYear( year ) )
				return 366;
			else
				return 365;
		}
		
		/**
		 * 해당 년도,월의 날수 
		 * @param year
		 * @param month
		 * @return day of the month
		 */ 
		static public function daysInMonth( year:int, month:int ):int
		{
			var monthLength:Array = 
			[     
				31, 28, 31, 30, 31, 30,
    			31, 31, 30, 31, 30, 31
    		];
    		
    		if( ALDate.isLeapYear( year ) )
    			monthLength[1]++;
    		return monthLength[ month-1 ];
		}
		
		/**
		 * 요일 (Julian Date 값으로 계산)
		 * @param Julian Date 
		 * @return 
		 * 	day of the week
		 * 	<p>
		 * 	0 : ALDayOfWeek.SUNDAY<br>
		 * 	1 : ALDayOfWeek.MONDAY<br>
		 * 	2 : ALDayOfWeek.THUSEDAY<br>
		 * 	3 : ALDayOfWeek.WEDNESDAY<br>
		 * 	4 : ALDayOfWeek.THURSDAY<br>
		 * 	5 : ALDayOfWeek.FRIDAY<br>
		 * 	6 : ALDayOfWeek.SATURDAY<br>
		 * 	</p>	
 		 * @see Astronomical Algorithms in Meeus, P65
		 */ 
		static public function dayOfWeekFromJD( jd:Number ):int
		{
			return Math.floor(jd + 1.5) % 7;
		}		
		
		/**
		 * 요일 (날짜 값으로 계산)
		 * @param year
		 * @param month
		 * @param day
		 * @return 
		 * 	day of the week
		 * 	<p>
		 * 	0 : ALDayOfWeek.SUNDAY<br>
		 * 	1 : ALDayOfWeek.MONDAY<br>
		 * 	2 : ALDayOfWeek.THUSEDAY<br>
		 * 	3 : ALDayOfWeek.WEDNESDAY<br>
		 * 	4 : ALDayOfWeek.THURSDAY<br>
		 * 	5 : ALDayOfWeek.FRIDAY<br>
		 * 	6 : ALDayOfWeek.SATURDAY<br>
		 * 	</p>		 
		 */ 
		static public function dayOfWeekFromDate( year:int, month:int, day:int ):int
		{
			var date:ALDate = new ALDate( year, month, day );
			return date.dayOfWeek;
		}

	}
}